package com.zg.indiv.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zg.common.core.dao.indiv.*;
import com.zg.common.core.dao.indiv.entity.*;
import com.zg.common.core.exception.BusinessException;
import com.zg.indiv.constants.IndivConstant;
import com.zg.indiv.constants.IndivExceptionEnum;
import com.zg.indiv.domain.bo.IndivFolderListDto;
import com.zg.indiv.domain.req.folder.*;
import com.zg.indiv.service.IndivFolderService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
* @author lixin
*/
@Service
@RequiredArgsConstructor
public class IndivFolderServiceImpl implements IndivFolderService {

    private final IndivFolderDao indivFolderDao;
    private final IndivSpaceDao indivSpaceDao;
    private final IndivDataTypeDao indivDataTypeDao;
    private final IndivFolderDataDao indivFolderDataDao;
    private final IndivSelectorFormDao indivSelectorFormDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addFirst(IndivFolderAddFirstReq param) {
        IndivSpace indivSpace = indivSpaceDao.getById(param.getSpaceId());
        if (indivSpace == null) {
            BusinessException.happen(IndivExceptionEnum.SPACE_NO.getCode(),
                    IndivExceptionEnum.SPACE_NO.getMsg());
        }
        long nameCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, IndivConstant.INDIV_FOLDER_PARENT_ID)
                        .eq(IndivFolder::getSpaceId, param.getSpaceId())
                        .eq(IndivFolder::getName, param.getName())
        );
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NAME_ERROR.getCode(),
                    IndivExceptionEnum.FOLDER_NAME_ERROR.getMsg());
        }

        IndivFolder indivFolder = new IndivFolder();
        indivFolder.setSpaceId(param.getSpaceId());
        indivFolder.setParentId(IndivConstant.INDIV_FOLDER_PARENT_ID);
        indivFolder.setName(param.getName());
        indivFolder.setSearchStr(param.getName());
        indivFolderDao.save(indivFolder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addNext(IndivFolderAddNextReq param) {
        IndivFolder parentIndivFolder = indivFolderDao.getById(param.getFolderId());
        if (parentIndivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }

        long dataTypeCount = indivDataTypeDao.count(
                new LambdaQueryWrapper<IndivDataType>()
                        .eq(IndivDataType::getFolderId, param.getFolderId())
        );
        if (dataTypeCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_HAVE_DATA_TYPE_NOT_ADD.getCode(),
                    IndivExceptionEnum.FOLDER_HAVE_DATA_TYPE_NOT_ADD.getMsg());
        }

        long nameCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, param.getFolderId())
                        .eq(IndivFolder::getName, param.getName())
        );
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NAME_ERROR.getCode(),
                    IndivExceptionEnum.FOLDER_NAME_ERROR.getMsg());
        }

        IndivFolder indivFolder = new IndivFolder();
        indivFolder.setSpaceId(parentIndivFolder.getSpaceId());
        indivFolder.setParentId(param.getFolderId());
        indivFolder.setName(param.getName());
        indivFolder.setSearchStr(indivFolder.getName());
        indivFolderDao.save(indivFolder);
    }

    @Override
    public List<IndivFolder> list(IndivFolderListDto param) {
        return indivFolderDao.list(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(param.getSpaceId() != null, IndivFolder::getSpaceId, param.getSpaceId())
                        .eq(param.getParentId() != null, IndivFolder::getParentId, param.getParentId())
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(List<Long> idList) {
        long childrenCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .in(IndivFolder::getParentId, idList)
        );
        if (childrenCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_HAVE_FOLDER_NOT_DEL.getCode(),
                    IndivExceptionEnum.FOLDER_HAVE_FOLDER_NOT_DEL.getMsg());
        }

        long dataCount = indivFolderDataDao.count(
                new LambdaQueryWrapper<IndivFolderData>()
                        .in(IndivFolderData::getFolderId, idList)
        );
        if (dataCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_HAVE_DATA_NOT_DEL.getCode(),
                    IndivExceptionEnum.FOLDER_HAVE_DATA_NOT_DEL.getMsg());
        }

        long selectorFormCount = indivSelectorFormDao.count(
                new LambdaQueryWrapper<IndivSelectorForm>()
                        .in(IndivSelectorForm::getFolderId, idList)
        );
        if (selectorFormCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_HAVE_DATA_NOT_DEL.getCode(),
                    IndivExceptionEnum.FOLDER_HAVE_DATA_NOT_DEL.getMsg());
        }

        indivFolderDao.removeByIds(idList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editName(IndivFolderEditNameReq param) {
        IndivFolder indivFolder = indivFolderDao.getById(param.getFolderId());
        if (indivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }
        if (indivFolder.getName().equals(param.getNewName())) {
            return;
        }

        long nameCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, indivFolder.getParentId())
                        .eq(IndivFolder::getName, param.getNewName())
        );
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NAME_ERROR.getCode(),
                    IndivExceptionEnum.FOLDER_NAME_ERROR.getMsg());
        }

        indivFolder.setName(param.getNewName());
        indivFolder.setSearchStr(indivFolder.getName() + indivFolder.getTags());
        indivFolderDao.updateById(indivFolder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeCopy(IndivFolderExecuteCopyReq param) {
        IndivFolder oldIndivFolder = indivFolderDao.getById(param.getFolderId());
        if (oldIndivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_COPY_NO.getCode(),
                    IndivExceptionEnum.FOLDER_COPY_NO.getMsg());
        }

        IndivFolder destIndivFolder = indivFolderDao.getById(param.getDestFolderId());
        if (destIndivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_DEST_NO.getCode(),
                    IndivExceptionEnum.FOLDER_DEST_NO.getMsg());
        }

        if (!oldIndivFolder.getSpaceId().equals(destIndivFolder.getSpaceId())) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_SPACE_NOT_COPY.getCode(),
                    IndivExceptionEnum.FOLDER_SPACE_NOT_COPY.getMsg());
        }

        long nameCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, param.getDestFolderId())
                        .eq(IndivFolder::getName, oldIndivFolder.getName())
        );
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_DEST_NAME_ERROR.getCode(),
                    IndivExceptionEnum.FOLDER_DEST_NAME_ERROR.getMsg());
        }

        copy(
                destIndivFolder.getSpaceId(),
                destIndivFolder.getSpaceId(),
                oldIndivFolder.getId(),
                oldIndivFolder.getName()
        );
    }

    private void copy(Long spaceId, Long destFolderId, Long oldFolderId, String name) {
        IndivFolder indivFolder = new IndivFolder();
        indivFolder.setSpaceId(spaceId);
        indivFolder.setParentId(destFolderId);
        indivFolder.setName(name);
        indivFolderDao.save(indivFolder);

        List<IndivDataType> indivDataTypes = indivDataTypeDao.list(
                new LambdaQueryWrapper<IndivDataType>()
                        .eq(IndivDataType::getFolderId, oldFolderId)
        );
        if (indivDataTypes.size() > 0) {
            for (IndivDataType indivDataType : indivDataTypes) {
                indivDataType.setFolderId(indivFolder.getId());
            }
            indivDataTypeDao.updateBatchById(indivDataTypes);
            return;
        }

        List<IndivFolder> nextFolders = indivFolderDao.list(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, oldFolderId)
        );
        for (IndivFolder nextFolder : nextFolders) {
            copy(spaceId, indivFolder.getId(), nextFolder.getId(), nextFolder.getName());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeMove(IndivFolderExecuteMoveReq param) {
        IndivFolder oldIndivFolder = indivFolderDao.getById(param.getFolderId());
        if (oldIndivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_MOVE_NO.getCode(),
                    IndivExceptionEnum.FOLDER_MOVE_NO.getMsg());
        }

        IndivFolder destIndivFolder = indivFolderDao.getById(param.getDestFolderId());
        if (destIndivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_DEST_NO.getCode(),
                    IndivExceptionEnum.FOLDER_DEST_NO.getMsg());
        }

        if (!oldIndivFolder.getSpaceId().equals(destIndivFolder.getSpaceId())) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_SPACE_NOT_MOVE.getCode(),
                    IndivExceptionEnum.FOLDER_SPACE_NOT_MOVE.getMsg());
        }

        long nameCount = indivFolderDao.count(
                new LambdaQueryWrapper<IndivFolder>()
                        .eq(IndivFolder::getParentId, destIndivFolder.getId())
                        .eq(IndivFolder::getName, oldIndivFolder.getName())
        );
        if (nameCount > 0) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_DEST_NAME_ERROR.getCode(),
                    IndivExceptionEnum.FOLDER_DEST_NAME_ERROR.getMsg());
        }

        oldIndivFolder.setParentId(destIndivFolder.getId());
        indivFolderDao.updateById(oldIndivFolder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeBindTag(IndivFolderExecuteBindTagReq param) {
        IndivFolder indivFolder = indivFolderDao.getById(param.getFolderId());
        if (indivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }

        indivFolder.setTags(StringUtils.join(param.getTags(),","));
        indivFolder.setSearchStr(indivFolder.getName() + indivFolder.getTags());
        indivFolderDao.updateById(indivFolder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeBindData(IndivFolderExecuteBindDataReq param) {
        IndivFolder indivFolder = indivFolderDao.getById(param.getFolderId());
        if (indivFolder == null) {
            BusinessException.happen(IndivExceptionEnum.FOLDER_NO.getCode(),
                    IndivExceptionEnum.FOLDER_NO.getMsg());
        }

        List<IndivFolderData> list = new ArrayList<>();
        for (Long dataId : param.getDataIds()) {
            IndivFolderData indivFolderData = new IndivFolderData();
            indivFolderData.setFolderId(param.getFolderId());
            indivFolderData.setDataId(dataId);
            list.add(indivFolderData);
        }
        indivFolderDataDao.saveBatch(list);
    }
}
